/*
 * <one line to give the program's name and a brief idea of what it does.>
 * Copyright (C) 2019
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "trophy.hpp"
#include "datatable.hpp"
#include "db.hpp"

#include <cmath>

uint32_t Trophy::levelFromPower(const uint32_t & power) {
	return std::floor(std::sqrt((power - 25) * 0.2) + 1);
}

uint32_t Trophy::levelFromResource(const uint32_t & value) {
	return std::floor(std::sqrt((value - 20) * 0.05) + 1);
}

uint32_t Trophy::maxLevel(const uint32_t & points) {
	uint32_t left = 0, right = tableSize::trophy - 1, mid;

	while(right - left > 1) {
		mid = left + (right - left) / 2;

		if(DataTable::trophyPriceTotal[mid] == points) {
			right = mid;
			break;
		} else if(DataTable::trophyPriceTotal[mid] > points) {
			right = mid - 1;
		} else {
			left = mid;
		}
	}

	if(DataTable::trophyPriceTotal[right] <= points) {
		mid = right;
	} else {
		mid = left;
	}

	return mid;
}

uint32_t Trophy::pointsFromLevelTotal(const uint32_t & level) {
	return DataTable::trophyTotal[level];
}

uint32_t Trophy::pointsBase(const PlayerData * data) {
	return trophyPoints(data, true, true);
}

uint32_t Trophy::pointsTest(const PlayerData * data, bool power) {
	return trophyPoints(data, power, false);
}

// private

uint32_t Trophy::trophyPoints(const PlayerData * data, bool power, bool base) {
	// NOTE AI trophy points have 2x multiplier
	uint32_t trophyPointsAI = 2 * Trophy::pointsFromLevelTotal(base ? data->aiBase : data->aiTest - 1);
	// power points
	uint32_t powerLevel = Trophy::levelFromPower(Trophy::powerPoints(data, base));
	uint32_t trophyPointsPower = power ? Trophy::pointsFromLevelTotal(powerLevel) : 0;

	// resource points
	resource res_new, res_old;

	if(!base) {
		/**
		 * \todo TODO заменить на ShipAI!
		 */
		res_new = DataTable::aiRewardTotal[data->aiTest - 1];
		res_old = DataTable::aiRewardTotal[data->aiBase];
	}

	// pcore
	uint32_t pcoreLevel = Trophy::levelFromResource(data->resBase.pcore + res_new.pcore - res_old.pcore);
	uint32_t trophyPointsPCore = Trophy::pointsFromLevelTotal(pcoreLevel);
	// ultium
	uint32_t ultiumLevel = Trophy::levelFromResource(data->resBase.ultium + res_new.ultium - res_old.ultium);
	uint32_t trophyPointsUltium = Trophy::pointsFromLevelTotal(ultiumLevel);
	// imatter
	uint32_t imatterLevel = Trophy::levelFromResource(data->resBase.imatter + res_new.imatter - res_old.imatter);
	uint32_t trophyPointsIMatter = Trophy::pointsFromLevelTotal(imatterLevel);

	return trophyPointsPower + trophyPointsAI + trophyPointsIMatter + trophyPointsPCore + trophyPointsUltium;
}

uint32_t Trophy::powerPoints(const PlayerData * data, bool base) {
	uint32_t points = 0;
	// power from ship parts
	// NOTE each ship have 80 base points + power from parts
	// total we have 3 ships + upgradeCount parts
	points += 80 * 3;

	for(auto i : (base ? data->buildBase.data : data->buildTest.data)) {
		points += DataTable::powerTotal[i];
	}

	// drone power
	for(auto i : data->drones) {
		points += DataTable::dronePower[i];
	}

	return points;
}

void Trophy::variants(DataBase * db, const PlayerData * data) {
	db->erase();

	uint8_t cap = tableSize::trophy - 1;
	uint8_t trophy[2] = {cap, cap};
	uint32_t points = Trophy::pointsTest(data, true);

	// NOTE if we have enough points ty max both params, skip search
	if(points >= DataTable::trophyPriceTotal[cap] * 2) {
		db->put((char *)trophy, sizeof(trophy));
		return;
	}

	uint8_t dmg;
	uint8_t start = points > DataTable::trophyPriceTotal[cap] ? Trophy::maxLevel(points - DataTable::trophyPriceTotal[cap]) : 0;

	for(uint8_t i = start; i <= cap; ++i) {
		if(points >= DataTable::trophyPriceTotal[i]) {
			dmg = Trophy::maxLevel(points - DataTable::trophyPriceTotal[i]);

// TODO FIX MISTAKE когда очков станет больше 75к будет ошибка, будет думать, что может купить 100 лвл
			if(points - DataTable::trophyPriceTotal[i] - DataTable::trophyPriceTotal[dmg] < DataTable::trophyPrice[i == cap ? cap : i + 1]
			        || i == cap
			  ) {
				trophy[0] = i, trophy[1] = dmg;
				db->put((char *)trophy, sizeof(trophy));
			}
		}
	}
}
